/*
 * Copyright 2011, 2012 Fabian Wenzelmann
 * 
 * This file is part of Recognition-Procedures-for-Boolean-Functions.
 * 
 * Recognition-Procedures-for-Boolean-Functions is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Recognition-Procedures-for-Boolean-Functions is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Recognition-Procedures-for-Boolean-Functions.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.wenzelmf.booleanFunction.lpb.combinatorial.occurrencePattern;

import java.io.Serializable;
import java.util.Iterator;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * This is an abstract base class for all implementations of occurrence patterns.
 * 
 * This class implements the Iterable interface for Integers and should return all occurrences in
 * ascending order. The Iterator may prohibit element removal (i. e. the remove method throws an
 * UnsupportedOperationException).
 * 
 * It also implements the Comparable interface (the {@link #compareTo(OccurrencePattern)} method
 * is defined in this class) and compares occurrence patterns according the &le; relation.
 * 
 * @author Fabian Wenzelmann
 * @version 1.0
 *
 */
public abstract class OccurrencePattern implements Serializable, Iterable<Integer>, Comparable<OccurrencePattern>
{
	private static final long serialVersionUID = 367026440458459319L;
	
	/*
	 * The name of the variable the occurrence pattern is created for (identifier of the variable).
	 */
	protected String variable;
	
	/**
	 * Constructs new occurrence pattern.
	 * 
	 * @param variable The name of the variable the occurrence pattern is created for (identifier of the variable).
	 */
	public OccurrencePattern(String variable)
	{
		this.variable = variable;
	}

	/**
	 * Add an occurrence of the respective variable in a clause of size {@code variableCount}.
	 * 
	 * @param variableCount The size of the clause the variables occurs in.
	 * 
	 * @return Should return the amount of how many clauses of size {@code variableCount} are
	 * present <i>after</i> adding {@code variableCount} one more time (-1 indicates that this implementation
	 * does not return the correct value).
	 */
	public abstract int addOccurrence(int variableCount);
	
	/**
	 * Returns a String representation of this Occurrence Pattern.
	 * 
	 * @return All occurrences of this occurrence pattern in curly brackets.
	 */
	@Override
	public String toString()
	{
		StringBuilder result = new StringBuilder("{");
		boolean first = true;
		for(Integer occurrence : this)
		{
			if(first)
			{
				first = false;
			}
			else
			{
				result.append(", ");
			}
			result.append(occurrence);
		}
		result.append("}");
		return result.toString();
	}
	
	/**
	 * Compares two occurrence patterns according the &le; relation.
	 * Returns 0 if {@code this} = {@code other} (they contain exactly the same elements), -1 if
	 * {@code this} &le; {@code other} and 1 if {@code other} &le; {@code this}. 
	 * 
	 * @return 0 if {@code this} = {@code other}, -1 if {@code this} &le; {@code other} and 1
	 * if {@code other} &le; {@code this}.
	 */
	@Override
	public int compareTo(OccurrencePattern other)
	{
		Iterator<Integer> m1 = this.iterator();
		Iterator<Integer> m2 = other.iterator();
		
		while(m1.hasNext() && m2.hasNext())
		{
			// get next element and check if they're equal
			int next1 = m1.next();
			int next2 = m2.next();
			
			if(next1 != next2)
			{
				// we've already found the longest common prefix so next1 and next2 are the "remainder" elements
				if(next2 < next1)
				{
					return -1;
				}
				else
				{
					return 1;
				}
			}
		}
		if(m1.hasNext())
		{
			// in this case the remainder of other is empty
			return 1;
		}
		if(m2.hasNext())
		{
			// this is empty
			return -1;
		}
		else
		{
			// they're both empty and therefore equal
			return 0;
		}
	}
	
	/**
	 * Indicates whether some other occurrence pattern is equal to this one (if they contain exactly
	 * the same elements).
	 * 
	 * @param other The object with which to compare. 
	 * 
	 * @return {@code true} if the occurrence patterns are equal ({@code this.compareTo(other) == 0}
	 * if {@code other} is also an occurrence pattern).
	 */
	@Override
	public boolean equals(Object other)
	{
		if(other == null)
			return false;
		if(!(other instanceof OccurrencePattern))
			return false;
		OccurrencePattern op = (OccurrencePattern)other;
		return this.compareTo(op) == 0;
	}

	/**
	 * Return the name of the variable the occurrence pattern was created for.
	 * 
	 * @return The name of the variable the occurrence pattern was created for.
	 */
	public String getVariable()
	{
		return variable;
	}
	
	public Element getXMLElement(Document document)
	{
		Element result = document.createElement("occurrencePattern");
		
		result.setAttribute("variable", getVariable());
		result.setAttribute("multiset", toString());
		return result;
	}
}
